package com.hmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.hmall.feign.client.ItemFeign;
import com.hmall.feign.common.dto.PageDTO;
import com.hmall.feign.common.pojo.Item;
import com.hmall.search.pojo.ItemDoc;
import com.hmall.search.pojo.RequestParams;
import com.hmall.search.service.SearchService;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    RestHighLevelClient client;


    @Override
    public PageDTO<ItemDoc> list(RequestParams params) {

        /*--------------------------------------查询--------------------------------------*/
        //创建一个搜索请求,指定要搜索得索引库
        SearchRequest searchRequest = new SearchRequest("hmall");
        //创建搜索源对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //获取查询条件
        QueryBuilder query = getQueryBuilder(params);

        // 添加相关性算分  竞价排名
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(query, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                        QueryBuilders.termQuery("isAD", true),
                        ScoreFunctionBuilders.weightFactorFunction(10)
                )
        });
        //指定查询条件
        sourceBuilder.query(functionScoreQuery);
        //分页
        int from = (params.getPage() - 1) * params.getSize();
        //分页得开始
        sourceBuilder.from(from);
        //每页展示数据得大小
        sourceBuilder.size(params.getSize());
        //排序
        //1.默认排序
        sourceBuilder.sort("_score", SortOrder.DESC);
        //2.销量排序
        if ("sold".equals(params.getSortBy())){
            sourceBuilder.sort("sold",SortOrder.DESC);
        }
        //3.价钱排序
        if ("price".equals(params.getSortBy())){
            sourceBuilder.sort("price",SortOrder.ASC);
        }
        //高亮显示
        if (!StringUtils.isEmpty(params.getKey())){
            //有查询关键字设置高亮
            //创建高亮生成器
            HighlightBuilder highlighterBuilder = new HighlightBuilder();
            //设置高亮字段
            highlighterBuilder.field("name");
            //如果要多个字段高亮,这项要为false
            highlighterBuilder.requireFieldMatch(false);
            sourceBuilder.highlighter(highlighterBuilder);
        }


        //指定查询源
        searchRequest.source(sourceBuilder);
        SearchResponse response = null;
        try {
            response = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //拿到最外层hits
        SearchHits searchHits = response.getHits();
        //拿到内层hits
        SearchHit[] hits = searchHits.getHits();
        //准备一个集合
        List<ItemDoc> docList = new ArrayList<>();
        //循环内层hits
        for (SearchHit hit : hits) {
            //拿到数据得json对象
            String sourceAsString = hit.getSourceAsString();
            //转换json成对象
            ItemDoc itemDoc = JSON.parseObject(sourceAsString, ItemDoc.class);
            //获取高亮
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField highlightField = highlightFields.get("name");
            if (highlightField != null){
                Text[] fragments = highlightField.fragments();
                itemDoc.setName(fragments[0].string());
            }
            //添加到上面准备得集合中
            docList.add(itemDoc);
        }
        //准备一个前端需要的返回对象
        PageDTO<ItemDoc> pageDto = new PageDTO<>();
        //填充数据
        pageDto.setList(docList);
        //填充数据
        pageDto.setTotal(searchHits.getTotalHits().value);
        //返回数据
        return pageDto;
    }

    /**
     * 获取搜索条件
     *
     * @param params
     * @return
     */
    private QueryBuilder getQueryBuilder(RequestParams params) {
        //构建一个搜索条件对象
        QueryBuilder query = QueryBuilders.boolQuery();
        //判断网页是否传来关键字
        if (StringUtils.isEmpty(params.getKey())) {
            //如果没有关键字  查询所有
            ((BoolQueryBuilder) query).must(QueryBuilders.matchAllQuery());
        } else {
            //如果有关键字   查询有关键字得内容
            ((BoolQueryBuilder) query).must(QueryBuilders.matchQuery("all", params.getKey()));
        }
        //添加过滤
        if (!StringUtils.isEmpty(params.getCategory())) {
            ((BoolQueryBuilder) query).filter(QueryBuilders.termQuery("category", params.getCategory()));
        }
        if (!StringUtils.isEmpty(params.getBrand())) {
            ((BoolQueryBuilder) query).filter(QueryBuilders.termQuery("brand", params.getBrand()));
        }
        if (!StringUtils.isEmpty(params.getMinPrice())) {
            ((BoolQueryBuilder) query).filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()*100));
        }
        if (!StringUtils.isEmpty(params.getMaxPrice())) {
            ((BoolQueryBuilder) query).filter(QueryBuilders.rangeQuery("price").lte(params.getMaxPrice()*100));
        }
        return query;
    }

    /**
     * 过滤条件展示
     *
     * @param params
     * @return
     */
    @Override
    public Map<String, List<String>> filters(RequestParams params) {

        SearchRequest request = new SearchRequest("hmall");
        //构建搜索源生成器
        SearchSourceBuilder searchSource = new SearchSourceBuilder();
        //指定搜索条件
        searchSource.query(getQueryBuilder(params));

        //指定聚合条件
        //创建一个聚合对象并取名
        AggregationBuilder categoryAgg = AggregationBuilders.terms("categoryAgg");
        //指定聚合的字段
        ((TermsAggregationBuilder) categoryAgg).field("category").size(20);
        //指定聚合对象
        searchSource.aggregation(categoryAgg);
        //创建一个聚合对象并取名
        AggregationBuilder brandAgg = AggregationBuilders.terms("brandAgg");
        //指定聚合的字段
        ((TermsAggregationBuilder) brandAgg).field("brand").size(20);
        //指定聚合对象
        searchSource.aggregation(brandAgg);

        //不获取原始文档
        searchSource.size(0);
        //指定查询源
        request.source(searchSource);
        SearchResponse response = null;
        try {
            response = client.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        Aggregations aggregations = response.getAggregations();

        //第一个聚合
        Terms categoryAggResult = aggregations.get("categoryAgg");
        //创建一个list集合
        List<String> cateList = new ArrayList<>();
        //遍历当前桶
        for (Terms.Bucket bucket : categoryAggResult.getBuckets()) {
            //将桶的数据key的value填充到cateList里
            cateList.add(bucket.getKeyAsString());
        }
        //第二个聚合
        Terms brandAggResult = aggregations.get("brandAgg");
        List<String> brandList = new ArrayList<>();
        for (Terms.Bucket bucket : brandAggResult.getBuckets()) {
            brandList.add(bucket.getKeyAsString());
        }
        //创建前端需要的Map集合
        Map<String, List<String>> result = new HashMap<>();
        //填充数据
        result.put("category",cateList);
        result.put("brand",brandList);
        //返回结果
        return result;
    }
}